/**
 * promise规范
 * 1 三种状态
 * pending
 * fulfilled
 * rejected
 * 备注： 状态只可以变更一次就凝固了
 * 2 有一个then方法
 * 1)接收两个参数onFulfilled, onRejected
 *
 */

const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

class MPromise {
  resolveCbs = [];
  rejectCbs = [];
  _status = PENDING;
  value = undefined;
  reason = undefined;

  constructor(fn) {
    try {
      fn(this.resolve.bind(this), this.reject.bind(this));
    } catch (error) {
      console.log('err', error);
      this.reject(error);
    }
  }

  get status() {
    return this._status;
  }

  set status(value) {
    this._status = value;
    switch (this.status) {
      case FULFILLED: {
        this.resolveCbs.forEach((cb) => cb());
        this.resolveCbs = [];
        break;
      }
      case REJECTED: {
        this.rejectCbs.forEach((cb) => cb());
        this.rejectCbs = [];
        break;
      }
      default:
        break;
    }
  }

  resolve(value) {
    if (this.status === PENDING) {
      this.value = value;
      this.status = FULFILLED;
    }
  }

  reject(reason) {
    console.log('reject status before ', this.status);
    if (this.status === PENDING) {
      this.reason = reason;
      this.status = REJECTED;
    }
  }

  isFunction(fn) {
    return typeof fn === 'function';
  }

  then(onFulfilled, onRejected) {
    // then的返回值依赖于onFulfilled或者onRejected方法的执行结果
    // 封装处理一下onFulfilled/onRejected的执行
    const onFulfilledWithCatch = (newPromise, resolve, reject) => {
      queueMicrotask(() => {
        try {
          if (!this.isFunction(onFulfilled)) {
            resolve(this.value);
          } else {
            const x = onFulfilled(this.value);
            this.resolvePromise(newPromise, x, resolve, reject);
          }
        } catch (error) {
          reject(error);
        }
      });
    };

    const onRejectedWithCatch = (newPromise, resolve, reject) => {
      queueMicrotask(() => {
        try {
          if (!this.isFunction(onRejected)) {
            reject(this.reason);
          } else {
            const x = onRejected(this.reason);
            this.resolvePromise(newPromise, x, resolve, reject);
          }
        } catch (error) {
          reject(error);
        }
      });
    };

    switch (this.status) {
      case FULFILLED: {
        var newPromise = new MPromise((resolve, reject) =>
          onFulfilledWithCatch(newPromise, resolve, reject),
        );
        return newPromise;
      }
      case REJECTED: {
        var newPromise = new MPromise((resolve, reject) =>
          onRejectedWithCatch(newPromise, resolve, reject),
        );
        return newPromise;
      }
      case PENDING: {
        // 收集起来
        var newPromise = new MPromise((resolve, reject) => {
          this.resolveCbs.push(() => onFulfilledWithCatch(newPromise, resolve, reject));
          this.rejectCbs.push(() => onRejectedWithCatch(newPromise, resolve, reject));
        });
        return newPromise;
      }
      default:
        break;
    }
  }

  resolvePromise(newPromise, x, resolve, reject) {
    // 处理三种情况
    // 避免死循环
    if (newPromise === x) {
      return reject(new TypeError('The promise and the return value are the same'));
    }

    if (x instanceof MPromise) {
      x.then((y) => {
        this.resolvePromise(newPromise, y, resolve, reject);
      }, reject);
    } else if ((x !== null && typeof x === 'object') || this.isFunction(x)) {
      let then;
      try {
        then = x.then;
      } catch (error) {
        reject(error);
      }
      if (this.isFunction(then)) {
        let called = false;
        try {
          then.call(
            x,
            (y) => {
              if (called) return;
              called = true;
              this.resolvePromise(newPromise, y, resolve, reject);
            },
            (err) => {
              if (called) return;
              called = true;
              reject(err);
            },
          );
        } catch (error) {
          if (called) return;
          reject(error);
        }
      } else {
        resolve(x);
      }
    } else {
      resolve(x);
    }
  }

  catch(onRejected) {
    return this.then(null, onRejected);
  }

  static resolve(value) {
    if (value instanceof MPromise) {
      return value;
    }
    if (value && typeof value === 'object' && typeof value.then === 'function') {
      let then = value.then;
      // let thisn = value.then;
      return new Promise((resolve, reject) => {
        then(resolve);
      });
    } else {
      return new Promise((resolve, reject) => resolve(value));
    }
  }

  static reject(value) {
    if (value && typeof value === 'object' && typeof value.then === 'function') {
      let then = value.then;
      return new Promise((resolve, reject) => {
        then(reject);
      });
    } else {
      return new Promise((resolve, reject) => reject(value));
    }
  }

  static all(promises) {
    return new Promise((resolve, reject) => {
      let fulfilledCount = 0;
      const itemNum = promises.length;
      const rets = Array.from({ length: itemNum });
      promises.forEach((promise, index) => {
        Promise.resolve(promise).then(
          (result) => {
            fulfilledCount++;
            rets[index] = result;
            if (fulfilledCount === itemNum) {
              resolve(rets);
            }
          },
          (reason) => reject(reason),
        );
      });
    });
  }

  static race(promises) {
    return new Promise(function (resolve, reject) {
      for (let i = 0; i < promises.length; i++) {
        Promise.resolve(promises[i]).then(
          function (value) {
            return resolve(value);
          },
          function (reason) {
            return reject(reason);
          },
        );
      }
    });
  }
}
function test() {
  const p = new MPromise((resolve, reject) => {
    setTimeout(() => {
      resolve(111);
    }, 500);
  });
  p.then((data) => {
    console.log('resolve: ', data);
    // return 322222;
    return new MPromise((r, re) => r(434343));
  })
    .then(
      (data) => {
        console.log('第二层resolve ', data);
      },
      (err) => {
        console.log('第二层err ', err);
      },
    )
    .catch((err) => {
      console.log('rejected: ', err);
    });
}
test();
